package com.example.hlsdemo.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.ExecutorService;

/**
 * FFmpeg工具类
 * 提供视频转HLS格式的功能
 */
@Slf4j
@Component
public class FFmpegUtil {

    public static final String HLS_DIR = "hls";

    /**
     * 使用FFmpeg将视频转换为HLS格式
     *
     * @param inputFile       输入视频文件
     * @param outputDir       HLS输出目录
     * @param uniqueId        唯一标识符
     * @param executorService 线程池，用于异步读取FFmpeg输出
     * @return 转换是否成功
     * @throws IOException          IO异常
     * @throws InterruptedException 中断异常
     */
    public static boolean convertToHLS(File inputFile, Path outputDir, String uniqueId, ExecutorService executorService)
            throws IOException, InterruptedException {

        // 创建HLS输出目录
        Path hlsDir = outputDir.resolve(HLS_DIR);
        Files.createDirectory(hlsDir);

        // 使用FFmpeg转换为HLS格式
        ProcessBuilder processBuilder = new ProcessBuilder(
                "ffmpeg",
                "-y", // 覆盖输出文件
                "-i", inputFile.getAbsolutePath(),

                // 高清流参数 (1080p)
                "-filter_complex", "[0:v]split=3[v1][v2][v3];[v1]scale=1920:1080[v1out];[v2]scale=1280:720[v2out];[v3]scale=854:480[v3out]",
                "-map", "[v1out]", "-map", "0:a",
                "-c:v", "libx264", "-preset", "superfast", "-crf", "23",
                "-c:a", "aac", "-b:a", "192k",
                "-threads", "0",
                "-f", "hls", "-hls_time", "10", "-hls_playlist_type", "vod",
                "-hls_segment_filename", hlsDir.resolve(uniqueId + "_high_%03d.ts").toString(),
                hlsDir.resolve(uniqueId + "_high.m3u8").toString(),

                // 中清流参数 (720p)
                "-map", "[v2out]", "-map", "0:a",
                "-c:v", "libx264", "-preset", "superfast", "-crf", "23",
                "-c:a", "aac", "-b:a", "128k",
                "-threads", "0",
                "-f", "hls", "-hls_time", "10", "-hls_playlist_type", "vod",
                "-hls_segment_filename", hlsDir.resolve(uniqueId + "_mid_%03d.ts").toString(),
                hlsDir.resolve(uniqueId + "_mid.m3u8").toString(),

                // 低清流参数 (480p)
                "-map", "[v3out]", "-map", "0:a",
                "-c:v", "libx264", "-preset", "superfast", "-crf", "23",
                "-c:a", "aac", "-b:a", "64k",
                "-threads", "0",
                "-f", "hls", "-hls_time", "10", "-hls_playlist_type", "vod",
                "-hls_segment_filename", hlsDir.resolve(uniqueId + "_low_%03d.ts").toString(),
                hlsDir.resolve(uniqueId + "_low.m3u8").toString()
        );

        // 记录完整的FFmpeg命令
        log.info("FFmpeg命令: {}", String.join(" ", processBuilder.command()));

        // 启动FFmpeg进程
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();

        // 异步读取FFmpeg输出
        StringBuilder output = new StringBuilder();
        executorService.submit(() -> {
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    log.info("FFmpeg输出: {}", line);
                    output.append(line).append("\n");
                }
            } catch (IOException e) {
                log.error("读取FFmpeg输出失败", e);
            }
        });

        // 等待FFmpeg进程完成
        int exitCode = process.waitFor();
        if (exitCode != 0) {
            log.error("FFmpeg转换失败，退出码: {}, 输出: {}", exitCode, output);
            return false;
        }

        return true;
    }

    /**
     * 将原始视频转为HLS格式（不转码，直接切片）
     * 适用于快速处理视频，让用户可以立即观看
     *
     * @param inputFile       输入视频文件
     * @param outputDir       HLS输出目录
     * @param uniqueId        唯一标识符
     * @param executorService 线程池
     * @return 转换是否成功
     */
    public static boolean convertToOriginalHLS(File inputFile, Path outputDir, String uniqueId, ExecutorService executorService)
            throws IOException, InterruptedException {
        // 创建HLS输出目录
        Path hlsDir = outputDir.resolve("hls");
        if (!Files.exists(hlsDir)) {
            Files.createDirectory(hlsDir);
        }

        // 使用FFmpeg转换为HLS格式,只对视频进行切片
        ProcessBuilder processBuilder = new ProcessBuilder(
                "ffmpeg",
                "-y",                    // 覆盖输出文件
                "-i", inputFile.getAbsolutePath(),
                "-c:v", "copy",         // 视频流直接复制，不转码
                "-c:a", "copy",         // 音频流直接复制，不转码
                "-f", "hls",
                "-hls_time", "10",      // 每个片段10秒
                "-hls_list_size", "0",  // 保留所有片段
                "-hls_playlist_type", "vod",
                "-hls_segment_type", "mpegts",  // 使用mpegts格式
                "-hls_flags", "independent_segments", // 独立分段
                "-hls_segment_filename", hlsDir.resolve(uniqueId + "_original_%03d.ts").toString(),
                hlsDir.resolve(uniqueId + "_original.m3u8").toString()
        );

        // 记录完整的FFmpeg命令
        log.info("FFmpeg原始切片命令: {}", String.join(" ", processBuilder.command()));

        // 启动FFmpeg进程
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();

        // 异步读取FFmpeg输出
        StringBuilder output = new StringBuilder();
        executorService.submit(() -> {
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    log.info("FFmpeg输出: {}", line);
                    output.append(line).append("\n");
                }
            } catch (IOException e) {
                log.error("读取FFmpeg输出失败", e);
            }
        });

        // 等待FFmpeg进程完成
        int exitCode = process.waitFor();
        if (exitCode != 0) {
            log.error("FFmpeg转换失败，退出码: {}, 输出: {}", exitCode, output);
            return false;
        }

        return true;
    }

    /**
     * 异步转码视频为多种清晰度
     *
     * @param inputFile          输入视频文件
     * @param outputDir          输出目录
     * @param uniqueId           唯一标识符
     * @param executorService    线程池
     * @param completionCallback 转码完成后的回调函数
     */
    public static void asyncConvertMultipleResolutions(File inputFile, Path outputDir, String uniqueId,
                                                       ExecutorService executorService, Runnable completionCallback) {
        executorService.submit(() -> {
            try {
                log.info("开始异步转码，视频ID: {}", uniqueId);

                // 创建HLS输出目录
                Path hlsDir = outputDir.resolve(HLS_DIR);
                if (!Files.exists(hlsDir)) {
                    Files.createDirectory(hlsDir);
                }

                // 使用FFmpeg转换为HLS格式
                ProcessBuilder processBuilder = new ProcessBuilder(
                        "ffmpeg",
                        "-y", // 覆盖输出文件
                        "-i", inputFile.getAbsolutePath(),

                        // 高清流参数 (1080p)
                        "-filter_complex", "[0:v]split=3[v1][v2][v3];[v1]scale=1920:1080[v1out];[v2]scale=1280:720[v2out];[v3]scale=854:480[v3out]",
                        "-map", "[v1out]", "-map", "0:a",
                        "-c:v", "libx264", "-preset", "superfast", "-crf", "23",
                        "-c:a", "aac", "-b:a", "192k",
                        "-threads", "0",
                        "-f", "hls", "-hls_time", "10", "-hls_playlist_type", "vod",
                        "-hls_segment_filename", hlsDir.resolve(uniqueId + "_high_%03d.ts").toString(),
                        hlsDir.resolve(uniqueId + "_high.m3u8").toString(),

                        // 中清流参数 (720p)
                        "-map", "[v2out]", "-map", "0:a",
                        "-c:v", "libx264", "-preset", "superfast", "-crf", "23",
                        "-c:a", "aac", "-b:a", "128k",
                        "-threads", "0",
                        "-f", "hls", "-hls_time", "10", "-hls_playlist_type", "vod",
                        "-hls_segment_filename", hlsDir.resolve(uniqueId + "_mid_%03d.ts").toString(),
                        hlsDir.resolve(uniqueId + "_mid.m3u8").toString(),

                        // 低清流参数 (480p)
                        "-map", "[v3out]", "-map", "0:a",
                        "-c:v", "libx264", "-preset", "superfast", "-crf", "23",
                        "-c:a", "aac", "-b:a", "64k",
                        "-threads", "0",
                        "-f", "hls", "-hls_time", "10", "-hls_playlist_type", "vod",
                        "-hls_segment_filename", hlsDir.resolve(uniqueId + "_low_%03d.ts").toString(),
                        hlsDir.resolve(uniqueId + "_low.m3u8").toString()
                );

                // 记录完整的FFmpeg命令
                log.info("异步FFmpeg命令: {}", String.join(" ", processBuilder.command()));

                // 启动FFmpeg进程
                processBuilder.redirectErrorStream(true);
                Process process = processBuilder.start();

                // 异步读取FFmpeg输出
                StringBuilder output = new StringBuilder();
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        log.info("异步FFmpeg输出: {}", line);
                        output.append(line).append("\n");
                    }
                }

                // 等待FFmpeg进程完成
                int exitCode = process.waitFor();
                if (exitCode != 0) {
                    log.error("异步FFmpeg转换失败，退出码: {}, 输出: {}", exitCode, output);
                } else {
                    log.info("异步FFmpeg转换成功，视频ID: {}", uniqueId);

                    // 调用完成回调
                    if (completionCallback != null) {
                        completionCallback.run();
                    }
                }
            } catch (Exception e) {
                log.error("异步转码出错，视频ID: " + uniqueId, e);
            }
        });
    }
} 